package vul

import (
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/DangoXJ/secbinarycheck-result-analyzer/logger"
	"go.uber.org/zap"
	"path/filepath"
	"strings"
)

var log *zap.Logger

func init() {
	log = logger.GetLogger("VUL")
}

func newOriginalFile() *excelize.File {
	f := excelize.NewFile()
	f.SetSheetName("Sheet1", DetailSheetName)

	dvRange := excelize.NewDataValidation(true)
	dvRange.Sqref = "H2:H65000"
	_ = dvRange.SetDropList([]string{ResultAffected, ResultUnaffected, ResultFalsePositive})

	err := f.AddDataValidation(DetailSheetName, dvRange)
	if err != nil {
		log.Error("设置数据校验失败", zap.String("err", err.Error()))
		return nil
	}

	dvRange = excelize.NewDataValidation(true)
	dvRange.Sqref = "I2:I65000"
	_ = dvRange.SetDropList([]string{ResultAffectedReasonTypeToBeFixed, ResultAffectedReasonTypeNoSolution,
		ResultAffectedReasonTypeOther, ResultUnaffectedReasonTypeFeatureNotUsed, ResultUnaffectedReasonTypePatched,
		ResultFalsePositiveReasonTypeIncorrectComponentVersion, ResultFalsePositiveReasonTypeCVENotMatched,
	})

	err = f.AddDataValidation(DetailSheetName, dvRange)
	if err != nil {
		log.Error("设置数据校验失败", zap.String("err", err.Error()))
		return nil
	}

	_ = f.SetSheetRow(DetailSheetName, "A1", &HeadersBefore)

	style, _ := f.NewStyle(StyleStringHeader)
	_ = f.SetCellStyle(DetailSheetName, "A1", "N1", style)

	style, _ = f.NewStyle(StyleStringHeaderConfirm)
	_ = f.SetCellStyle(DetailSheetName, "H1", "K1", style)

	return f
}

func newReleaseNotes() *excelize.File {
	f := excelize.NewFile()
	f.SetSheetName("Sheet1", DetailReleaseNotesSheetName)

	_ = f.SetSheetRow(DetailReleaseNotesSheetName, "A1", &HeadersReleaseNotes)

	style, _ := f.NewStyle(StyleStringHeader)
	_ = f.SetCellStyle(DetailReleaseNotesSheetName, "A1", "J1", style)

	return f
}

// InitFromReleaseNotes 从欧拉的Release Notes中读取对应的漏洞信息
func InitFromReleaseNotes(filename string, components map[string]string) map[string]map[string]string {
	log.Info("从欧拉ReleaseNotes中读取漏洞处理信息")

	baselines := make(map[string]map[string]string)

	f, err := excelize.OpenFile(filename)
	if err != nil {
		log.Error("从欧拉Release Notes中读取漏洞分析信息失败", zap.String("filename", filename))
		return nil
	}

	if f == nil {
		log.Error("从欧拉Release Notes中读取漏洞分析信息失败", zap.String("filename", filename))
		return nil
	}

	rows, err := f.Rows(DetailReleaseNotesSheetName)
	if err != nil {
		log.Error("从欧拉Release Notes中读取漏洞分析信息失败", zap.String("filename", filename),
			zap.String("sheet", DetailReleaseNotesSheetName))
		return nil
	}

	for rows.Next() {

		columns, _ := rows.Columns()
		// 从第二行开始分析
		if len(columns) < len(HeadersReleaseNotes) {
			continue
		}

		cve := strings.TrimSpace(columns[HeadersReleaseNotesIndexMap[HeaderReleaseNotesCVE]])
		if !strings.HasPrefix(cve, "CVE-") {
			continue
		}

		component := strings.TrimSpace(columns[HeadersReleaseNotesIndexMap[HeaderReleaseNotesComponent]])

		var isAffected string
		var reasonType string

		status := strings.TrimSpace(columns[HeadersReleaseNotesIndexMap[HeaderReleaseNotesConfirmStatus]])
		switch status {
		case "已修复":
			{
				isAffected = ResultUnaffected
				reasonType = ResultUnaffectedReasonTypePatched
			}
		case "不涉及":
			{
				isAffected = ResultUnaffected
				reasonType = ResultUnaffectedReasonTypeOther
			}
		case "不影响":
			{
				isAffected = ResultUnaffected
				reasonType = ResultUnaffectedReasonTypeOther
			}
		case "遗留":
			{
				isAffected = ResultAffected
				reasonType = ResultAffectedReasonTypeNoSolution
			}
		default:
			{
				isAffected = status
				reasonType = "TODO"
			}
		}

		remarks := strings.TrimSpace(columns[HeadersReleaseNotesIndexMap[HeaderReleaseNotesRemarks]])
		remarks = "源自" + filepath.Base(filename) + ":\r\n" + remarks

		confirmer := strings.TrimSpace(columns[HeadersReleaseNotesIndexMap[HeaderReleaseNotesConfirmer]])

		baselines[cve+"_"+component] = map[string]string{
			HeaderIsAffected: isAffected,
			HeaderReasonType: reasonType,
			HeaderConfirmer:  confirmer,
			HeaderRemarks:    remarks,
		}
		//logger.Info("测试一下", zap.Any("map", components), zap.String("component", component))
		if _, ok := components[component]; ok {
			baselines[cve+"_"+components[component]] = map[string]string{
				HeaderIsAffected: isAffected,
				HeaderReasonType: reasonType,
				HeaderConfirmer:  confirmer,
				HeaderRemarks:    remarks,
			}
		}
	}

	if len(baselines) == 0 {
		log.Error("未能读取到任何BinScope的分析结果", zap.String("filename", filename))
		return nil
	}
	log.Info("从欧拉ReleaseNotes中初始化成功")
	return baselines
}

// InitFromBaseline 从产品的分析基线中读取对应的漏洞信息
func InitFromBaseline(filename string) map[string]map[string]string {
	log.Info("从产品的分析基线中读取对应的漏洞信息", zap.String("filename", filename))

	baselines := make(map[string]map[string]string)

	f, err := excelize.OpenFile(filename)
	if err != nil || f == nil {
		log.Error("从产品的分析基线中读取漏洞分析信息失败", zap.Any("err", err))
		return nil
	}

	rows, err := f.Rows(DetailSheetName)
	if err != nil {
		log.Error("从产品的分析基线中读取漏洞分析信息失败", zap.Any("err", err),
			zap.String("sheet", DetailSheetName))
		return nil
	}

	for rows.Next() {

		columns, _ := rows.Columns()
		log.Info("columns", zap.Any("columns", columns), zap.Int("len", len(columns)))

		// 从第二行开始分析
		if len(columns) < len(HeadersBefore) {
			continue
		}
		log.Info("columns", zap.Any("columns", columns))

		cve := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderCVE]])
		if !strings.HasPrefix(cve, "CVE-") {
			continue
		}

		component := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderComponent]])

		isAffected := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderIsAffected]])
		reasonType := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderReasonType]])

		remarks := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderRemarks]])
		remarks = "源自" + filepath.Base(filename) + ":\r\n" + remarks

		confirmer := strings.TrimSpace(columns[HeadersBeforeIndexMap[HeaderConfirmer]])

		baselines[cve+"_"+component] = map[string]string{
			HeaderIsAffected: isAffected,
			HeaderReasonType: reasonType,
			HeaderConfirmer:  confirmer,
			HeaderRemarks:    remarks,
		}
	}

	if len(baselines) == 0 {
		log.Error("未能读取到任何BinScope的分析结果", zap.String("filename", filename))
		return nil
	}
	log.Info("从欧拉ReleaseNotes中初始化成功")
	return baselines
}

type Vul struct {
}

func NewVul() *Vul {
	return new(Vul)
}

func (v *Vul) AnalyseFromFile(filename string, baselines map[string]map[string]string) [][]string {
	log.Error("TODO", zap.String("filename", filename), zap.Int("len", len(baselines)))

	return [][]string{}

}

func (v *Vul) NewOriginalFile() *excelize.File {
	log.Error("TODO")
	return newOriginalFile()
}

func (v *Vul) UpdateSummaryFile(f *excelize.File, rows [][]string) {
	log.Error("TODO", zap.String("filename", f.Path), zap.Int("len", len(rows)))
}
